﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	/// <summary>
	/// This class is used to represent all bytecode operations that take a scalar value on the eval stack and evaluate to a new value on the eval stack.
	/// </summary>
	public sealed class IREvalStackTypeOp : IRByteCode
	{
		/// <summary>
		/// An array containing all the acceptable opcodes for this operation class (in order)
		/// </summary>
		public static readonly OpCode.Op[] AcceptableOps = { OpCode.Op.Conv, OpCode.Op.Box, OpCode.Op.UnBox, OpCode.Op.Cast, OpCode.Op.IsInst, OpCode.Op.Constrain };

		private readonly IRType m_typearg;

		private readonly int m_constrainpos;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant(this.m_typearg != null);
		}

		public IREvalStackTypeOp(OpCode.Op opc, IRType typearg, int optionalstackpos, IRSourceLocation sloc)
			: base(opc, sloc)
		{
			Contract.Requires(AcceptableOps.Contains(opc));
			Contract.Requires(typearg != null);

			this.m_typearg = typearg;
			this.m_constrainpos = optionalstackpos;
		}

		public override string ToString()
		{
			if(this.OperationCode == OpCode.Op.Conv)
				return "conv:     into " + this.m_typearg.ToString();
			else if(this.OperationCode == OpCode.Op.Box)
				return "box:      type=" + this.m_typearg.ToString();
			else if(this.OperationCode == OpCode.Op.UnBox)
				return "unbox:    type=" + this.m_typearg.ToString();
			else if(this.OperationCode == OpCode.Op.Cast)
				return "cast:     into " + this.m_typearg.ToString();
			else if(this.OperationCode == OpCode.Op.IsInst)
				return "instof:  " + this.m_typearg.ToString();
			else if(this.OperationCode == OpCode.Op.Constrain)
				return "constrain: @" + this.m_constrainpos.ToString() + " " + this.m_typearg.ToString();
			else
				return "--BAD OPCODE--";
		}

		/// <summary>
		/// The extra type parameter for this operation.
		/// </summary>
		public IRType TypeArg
		{
			get
			{
				Contract.Ensures(Contract.Result<IRType>() != null);

				return this.m_typearg;
			}
		}

		/// <summary>
		/// The position in the stack that needs to be constrained.
		/// </summary>
		public int ConstrainPos
		{ get { return this.m_constrainpos; } }
	}
}
